/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit.handlers;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.neIsInstance;
import static java.lang.Math.max;

import java.util.LinkedList;
import java.util.List;

import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.CombinedFragment;
import org.eclipse.uml2.uml.DestructionEvent;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.Lifeline;

import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.sequence.sedi.figures.SeDiSubpartFigure;
import com.cea.papyrus.diagram.sequence.sedi.model.command.SetConstraintCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * Handles automatic layout of Interaction frames.
 * 
 * @author frankd@ifi.uio.no
 */
public class InteractionLayoutHandler implements LayoutHandler<Interaction> {
    @SuppressWarnings("unchecked")
    public Rectangle layout(Diagram diagram, Interaction interaction) {
        int bottom = 10;
        for (Interaction i : (List<Interaction>) ModelUtil.getInteractions(diagram)) {
            GraphNode view = ModelUtil.getGraphNode(i, diagram);
            if (view != null && view.getPosition() != null && view.getSize() != null) {
                Rectangle bounds = new Rectangle(view.getPosition(), view.getSize());
                bottom = max(bounds.getBottom().y, bottom);
            }
        }

        // estimate width based on number of lifelines
        int width = interaction.getLifelines().size() * 140 + 40;

        return new Rectangle(10, 10, max(width, 450), max(bottom + 30, 250));
    }

    @SuppressWarnings("unchecked")
    public Command[] getMoveCommands(final EditPart interactionEP, final Rectangle suggestedBounds) {

        // SetConstraintCommand
        // Move the interaction.
        // SetConstraintCommand
        // Move each overlapping CombinedFragment the same amount
        // SetConstraintCommand
        // Resize contained lifelines (unless they are terminated)

        final List<Command> cmds = new LinkedList<Command>();

        final GraphNode interactionView = (GraphNode) interactionEP.getModel();
        final SeDiSubpartFigure interactionFigure =
                (SeDiSubpartFigure) ((GraphicalEditPart) interactionEP).getFigure();
        // final InteractionFragment fragment = interactionView.getTypedElement();

        // Step 1
        final Rectangle preferredBounds = interactionFigure.getPreferredBounds(suggestedBounds);
        cmds.add(new SetConstraintCommand(interactionView, preferredBounds));

        // Step 2, 3
        Rectangle interactionBounds = interactionFigure.getBounds();
        int deltax = preferredBounds.x - interactionBounds.x;
        int deltay = preferredBounds.y - interactionBounds.y;
        int deltaheight = preferredBounds.height - interactionBounds.height;
        int deltawidth = preferredBounds.width - interactionBounds.width;
        Diagram diagram = LookForElement.getDiagram(interactionView); // TODO replace
        // with
        // "getInteraction"
        for (GraphElement diagramElement : (List<GraphElement>) ModelUtil.getContents(diagram)) {
            // Step 2
            if (neIsInstance(diagramElement, CombinedFragment.class)) {
                final GraphNode fragmentView = (GraphNode) diagramElement;
                final Rectangle fragmentBounds =
                        new Rectangle(fragmentView.getPosition(), fragmentView.getSize());
                fragmentBounds.translate(deltax, deltay);
                if (fragmentBounds.x < interactionBounds.x
                        && fragmentBounds.width > interactionBounds.width) // extraglobal
                    // CF
                    fragmentBounds.resize(deltawidth, 0);

                cmds.add(new SetConstraintCommand(fragmentView, fragmentBounds));
            }

            // Step 3
            else if (neIsInstance(diagramElement, Lifeline.class)) {
                final GraphNode lifelineView = (GraphNode) diagramElement;
                Lifeline lifeline = ModelUtil.<Lifeline> getSemantic(lifelineView);
                List coveredBys = lifeline.getCoveredBys();
                if (coveredBys.isEmpty()
                        || !(coveredBys.get(coveredBys.size() - 1) instanceof DestructionEvent)) {
                    final Rectangle lifelineBounds =
                            new Rectangle(lifelineView.getPosition(), lifelineView.getSize());
                    if (interactionBounds.height - lifelineBounds.bottom() < 20) {
                        lifelineBounds.height += deltaheight;

                        cmds.add(new SetConstraintCommand(lifelineView, lifelineBounds));
                    }
                }
            }
        }

        return cmds.toArray(new Command[cmds.size()]);
    }
}
